/*
 * Fuse.c
 *
 *  Created on: 06/11/2011
 *      Author: tacho
 */

#include "headers/GlobalHeaderPFS.h"
#include "headers/Constantes_PFS.h"

pthread_mutex_t mutex_array_conexiones;
pthread_mutex_t mutex_caches;
char * handlerArchivoMapeado;
pthread_mutex_t mutex_array_nrosClusters;
tNodoCache * lCaches;
mbr bootSector;
uint32_t *fatRegion;
t_conexion * array_conexiones;
struct_config configuracion;
int32_t filesize;
int32_t cantBloquesXCache;
pthread_mutex_t mutex_fatRegion;
//pthread thread_msync_id;
volatile sig_atomic_t dump=0;
int32_t contador_msync;
t_log *logger;
pthread_t thread_msync_id,thread_consola;
int32_t sali;
tDirectoryEntry entradaDelDirectorioActual;
int32_t cantEntradasDir;
tnodoCacheEntradasDir * cacheEntradasDir;
int32_t cantEntradasDirMax;
pthread_mutex_t mutex_cache_entradasDir;

//Aclaraciones:
//1) ENOENT A directory component in pathname would have been accessible but does not exist or was a dangling symbolic link.
//2) hay funciones que todavia no se crearon y se las dejan como comentario de sus posibles encabezados
//3) donde son necesarias funciones que abarcarian cache, solo hay comentarios de sus posibles encabezados nada mas
//4) todavia faltan los encabezados para "rmdir" y "unlink", tira error de no entender dicha operacion

static int fuse_truncate (const char * path, off_t tamano){

	//log_debug(logger,"PFS","END WRITE archivo %s\nsize:%d\noffset%d\n",path,size,offset);
	if(configuracion.logsActivados)
	log_info(logger, "PFS", "Inicio de Truncate: %s a un tamaño de %i bytes\n",path,tamano);
	pfs_truncar(path,tamano);
	if(configuracion.logsActivados)
	log_info(logger, "PFS", "Finalización de Truncate: %s a un tamaño de %i bytes\n",path,tamano);

	return 0;
}
static int fuse_rename(const char * pathOrigen,const char * pathDestino){
	if(configuracion.logsActivados)
	log_info(logger, "PFS", "Inicio de Rename: de %s  a %s \n",pathOrigen,pathDestino);
	pfs_mover(pathOrigen,pathDestino);
	if(configuracion.logsActivados)
	log_info(logger, "PFS", "Final de Rename:: de %s  a %s \n",pathOrigen,pathDestino);

	return 0;
}
static int fuse_getattr(const char *path, struct stat *stats)
{
	//atributos de un directorio o archivo
	//stats donde debo devolver los stats
	tDirectoryEntries entradasArch;
	 if (strcmp(path,"/")!=0){
			if (!obtenerEntradaAPartirDePATH(path,&entradasArch)){
					return -ENOENT;}
	 }else{
		stats->st_size= TAMANO_CLUSTER;
		stats->st_mode=S_IFDIR | 0755;
		stats->st_nlink=2;
		return 0;
	 }

	memset(stats, 0, sizeof(struct stat));
	*stats = pfs_getatri(entradasArch);
	//memcpy(stats,(),sizeof(struct stat));

	return 0;
}

static int fuse_readdir(const char *path, void *buf, fuse_fill_dir_t fill,
					   off_t offset, struct fuse_file_info *info)
{
	//listar dir
	//buf: contenido del dir
	//fill:func xra cargar el contenido en buf
	// (void) offset, fi; no encuentro funcion

	//char * nombreArch;
	tDirectoryEntries entradaPrincipal;

	tListaEntradasDeDirectorios * entradas;
	tListaEntradasDeDirectorios * temp;


	if (obtenerEntradaAPartirDePATH(path,&entradaPrincipal)){
		entradas = cargarListaDeEntradasAPartirDe(obtenerDireccionPrimerCluster(&entradaPrincipal.directoryEntry));
	}else{
		return -ENOENT;
	}
	//Viendo ejemplos creo que los pathsrelativos los carga del sistema
	//si es asi, hay que modificar el cargarListaDeEntradas porque utiliza una variable global
	//que se creo para la pruebas
	fill(buf, ".", NULL, 0);
	fill(buf, "..", NULL, 0);
	temp=entradas;
	char nombre[20];
	while(temp!=NULL){
		//nombreArch = NULL;
		memset(&nombre,0,sizeof(nombre));
		nombreDelDirectorio(temp,nombre);
		//strcpy(nombre,nombreArch);
		//if(strcmp(nombre,".")&&strcmp(nombre,".."))
		fill(buf,nombre,NULL,0);
		temp=temp->sgte;
		//free(nombreArch);
	}
	borrarListasDeEntradas(&entradas);
	return 0;
}

static int fuse_open(const char *path, struct fuse_file_info *info)
{

	tDirectoryEntries entradasArch;
	if(configuracion.tamanoCache>0){
		if(obtenerEntradaAPartirDePATH(path,&entradasArch)){
			tNodoCache * cache=conseguirCache(path);
				if(cache==NULL) crearCache(path);
		}
		if(configuracion.logsActivados)
	log_info(logger, "PFS", "Open: de %s\n",path);
	int32_t nroCluster = obtenerDireccionPrimerCluster(&(entradasArch.directoryEntry));
	if(configuracion.logsActivados){
		while (nroCluster!=FAT_ULTIMO && nroCluster!=FAT_ULTIMO1){
		         log_debug(logger,"PFS","Clusters de %s: %i,\n",path,nroCluster);
		         nroCluster = obtenerSiguienteCluster(nroCluster);
		   }
		}
	}

    return 0;
}

static int fuse_read(const char *path, char *buffer, size_t tamano, off_t offset,
					struct fuse_file_info *info)
{
	//lee arch y devuelvo
	//buf: cont arch
	//tamano: lo que se quiere leer
	//offset: inicio
	//(void) info; no me sirve creo
	if(configuracion.logsActivados)
	log_info(logger, "PFS", "Inicio de Read: del %s  desde el offset %i \n",path,(int32_t) offset);
	uint32_t tamanoLeido = pfs_read(path,buffer,tamano,offset);
	if(configuracion.logsActivados)
	log_info(logger, "PFS", "Final de Read: del %s  de un tamaño de %i \n",path,tamano);

	return tamanoLeido;
}
static int fuse_write(const char *path, const char *buffer, size_t tamano, off_t offset,
		struct fuse_file_info *info)
{
	if(configuracion.logsActivados)
	log_info(logger, "PFS", "Inicio de Write: del %s  desde el offset %i \n",path,(int32_t) offset);
	int tamanoEscrito = pfs_write(path,buffer,tamano,offset);
	if(configuracion.logsActivados)
	log_info(logger, "PFS", "Final de Write: del %s de un tamaño de  %i \n",path,tamano);
	return tamanoEscrito;

}

static int fuse_mkdir(const char * path,mode_t modo){
	//tDirectoryEntries entradasDelArchivo;
	//const char * pathPadre = eliminarUltimoSubPaths(path);
	//if((obtenerEntradaAPartirDePATH(path,&entradasDelArchivo)!=1) && (obtenerEntradaAPartirDePATH(pathPadre,&entradasDelArchivo)==1)){
			pfs_mkDir(path);
	//	}
	return 0;
}
static int fuse_create(const char * path, mode_t modo, struct fuse_file_info * info){
	//involucra abrirlo tambien? todavia no se bien la aplicacion de dicha funcion
	//es decir si abarcaria la creacion de una cache o algo mas
	pfs_mkFile(path);

	return 0;
}

static int fuse_flush(const char * path, struct fuse_file_info * fi){
	//todavia no hay cache, menos va haber flush jaja

	if(configuracion.tamanoCache>0){
		tNodoCache * cache=conseguirCache(path);
		if(cache!=NULL)	pfs_flush(path);
		if(configuracion.logsActivados)
		log_info(logger, "PFS", "Flush: de %s\n",path);
	}


	return 0;
}

static int fuse_release(const char * path, struct fuse_file_info * fi){
	//supone la eliminacion de una cache peor todavia jaja
	if(configuracion.tamanoCache>0){
		tNodoCache * cache=conseguirCache(path);
		if(cache!=NULL)	pfs_release(path);
		if(configuracion.logsActivados)
		log_info(logger, "PFS", "Release: de %s\n",path);
	}

	return 0;
}

static int fuse_rmdir(const char *path){
	tDirectoryEntries entradasDelArchivo;
	char pathPadre[50];
	eliminarUltimoSubPaths(path,pathPadre);
	if((obtenerEntradaAPartirDePATH(path,&entradasDelArchivo)==1) && (obtenerEntradaAPartirDePATH(pathPadre,&entradasDelArchivo)==1)){
		pfs_rmDir(path);
	}

	return 0;
}

static int fuse_unlink(const char *path){
	//remover file

	pfs_unlink(path);

	return 0;
}

static struct fuse_operations operaciones_pfs = {
	.truncate = fuse_truncate,
	.rename = fuse_rename,
	.getattr   = fuse_getattr,
	.readdir = fuse_readdir,
	.open   = fuse_open,
	.read   = fuse_read,
	.write = fuse_write,
	.mkdir = fuse_mkdir,
	.create = fuse_create,
	.flush = fuse_flush,
	.release = fuse_release,
	.rmdir = fuse_rmdir,
	.unlink =fuse_unlink,
};


int main(int argc, char *argv[])
{

	inicializar();

	pthread_create(&thread_consola,NULL,(void*)&consolaPFS,NULL);
	fuse_main(argc,argv,&operaciones_pfs,NULL);

	finalizar();

	return 0;

}

